﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace AMOOF {
    public static class Reflector {


        public static T GetClass<T>(string name) {
            object o = new object();
            try {
                o = (T)Activator.CreateInstance(Type.GetType(name));
            }
            catch (Exception ex) {
                Program.log(ex.Message + "\r\n" + ex.StackTrace);
            }
            
            return (T)o;
        }

        public static T GetField<T>(string name, object parent) {
            object o = new object();

            try {
                o = (T)parent.GetType().GetField(name, BindingFlags.NonPublic | BindingFlags.Instance).GetValue(parent);
            }
            catch (Exception ex) {
                Program.log(ex.Message + "\r\n" + ex.StackTrace);
            }

            return (T)o;
        }

        public static MethodInfo GetMethod(string name, object parent) {
            object o = new object();

            try {
                o = (MethodInfo)parent.GetType().GetMethod(name, BindingFlags.NonPublic | BindingFlags.Instance);
            }
            catch (Exception ex) {
                Program.log(ex.Message + "\r\n" + ex.StackTrace);
            }
            return (MethodInfo)o;
        }

        public static T GetEnum<T>(string name) {
            object o = new object();

            try {
                o = (T)Enum.Parse(typeof(T), name);
            }
            catch (Exception ex) {
                Program.log(ex.Message + "\r\n" + ex.StackTrace);
            }

            return (T)o;
        }

        public static T GetResource<T>(string name) {
            object o = new object();
            try {
                o = (T)Properties.Resources.ResourceManager.GetObject(name);
            }

            catch (Exception ex) {
                Program.log(ex.Message + "\r\n" + ex.StackTrace);
            }

            return (T)o;
        }

        

        /// <summary>
        /// This is most-useful for primitive data types. 
        /// It is not recommended to use this for any other reason.
        /// Simply pass in the variable whose value you want to update, the Class it is contained in, and the value to update it with.
        /// By implementing an anonymous data type, it allows us to add much flexibility.
        /// </summary>
        /// <typeparam name="T">The type that the value gets cast to.</typeparam>
        /// <param name="name">The name of the field or variable.</param>
        /// <param name="parent">The Class object containing the field you want to modify.</param>
        /// <param name="val">The value, casted by T, used to update the variable.</param>
        public static void SetField<T>(string name, object parent, T val) {
            try {
                parent.GetType().GetField(name, BindingFlags.NonPublic | BindingFlags.Instance).SetValue(parent, val);
            }
            catch (Exception ex) {
                Program.log(ex.Message + "\r\n" + ex.StackTrace);
            }
        }

        /// <summary>
        /// This invokes a non-public method by name. If it doesn't exist, this won't do anything.
        /// If you are attempting to call public methods, you may want to rethink how you are designing your program.
        /// This also returns the invoked method's value.
        /// </summary>
        /// <param name="name">The name of the method or function.</param>
        /// <param name="parent">The Class object containing the method you want to invoke.</param>
        /// <param name="parameters">The parameters that should be passed to the method</param>
        public static object InvokeMethod(string name, object parent, object[] parameters) {
            object o = new object();
            try {
                o = parent.GetType().GetMethod(name, BindingFlags.NonPublic | BindingFlags.Instance).Invoke(parent, new object[] { parameters });
            }
            catch (Exception ex) {
                Program.log(ex.Message + "\r\n" + ex.StackTrace);
            }

            return o;
        }
    }
}
